/*!
 * jSmobile Javascript Library v2.0.0 
 *
 * Copyright 2017, Frank Chao
 * Released under the MIT license
 * 
 * Only support webkit core of mobile device [ iOS9.0+ and Android4.4+ ( chrome40.0+ ) ]
 *
 * Inspired by jQuery and Dom7 Library in Framework7 
 *
 * Released on: May 09, 2017
 */

!(function ( window, factory ) {

    if ( typeof define === "function" && define.amd ) { 
        define(factory);
    } else if ( typeof module === "object" && typeof module.exports === "object" ) {
        module.exports = factory();
    } else {
        window.jS = factory();
    }

})( this, function () {

    var version = "2.0.0";

    var doc = document;

    var fn = {
        optc: function ( obj ) {
            return Object.prototype.toString.call(obj).replace(/(\[object |\])/g, "");
        },
        isFunc: function ( callback ) {
            return !!(fn.optc(callback) === "Function");
        },
        isStr: function ( str ) {
            return !!(fn.optc(str) === "String");
        },
        isPlainObj: function ( obj ) {
            return !!(fn.optc(obj) === "Object");
        },
        isBool: function ( bool ) {
            return !!(fn.optc(bool) === "Boolean");
        },
        isNum: function ( num ) {
            return !!(fn.optc(num) === "Number");
        },
        formatSpace: function ( str ) { 
            return str.replace(/\s+/g, " ").replace(/(^\s*)|(\s*$)/g, "").split(" "); 
        },
        formatConverter: function ( str ) { 
            return str.replace(/([A-Z])/g, "-$1").toLowerCase(); 
        },
        doClass: function ( classes, type, that ) {
            classes = fn.formatSpace(classes);
            for ( var i = 0, j = classes.length; i < j; i++ ) {
                that.classList[type](classes[i]);
            }
        },
        trim: function ( str, pos ) {
            if ( fn.isStr(str) ) {
                if ( pos == "all" ) {
                    return str.replace(/\s+/g, "");
                }
                if ( pos == "left" ) {
                    return str.replace(/^\s*/g, "");
                }
                if ( pos == "right" ) {
                    return str.replace(/\s*$/g, "");
                }
                if ( pos == "bothSide" ) {
                    return str.replace(/(^\s*)|(\s*$)/g, "");
                }
            }
        },
        swipe: function ( direction, target, callback ) {
            var startX, startY, x, y, xx, yy, isMove;
            var that = jS(target)[0];
            that.addEventListener("touchstart", function ( e ) {
                isMove = false;
                startX = e.targetTouches[0].pageX;
                startY = e.targetTouches[0].pageY;
            });
            that.addEventListener("touchmove", function ( e ) {
                xx = e.changedTouches[0].pageX;
                yy = e.changedTouches[0].pageY;
                if ( xx != startX || yy != startY ) {
                    e.preventDefault();
                    (direction == "") && callback.apply(this, [xx, yy]);
                }
            });
            that.addEventListener("touchend", function ( e ) {
                x = e.changedTouches[0].pageX;
                y = e.changedTouches[0].pageY;
                if ( x - startX < -30 && direction == "left" ) {
                    isMove = true;
                    callback.apply(this);
                }
                if ( x - startX > 30 && direction == "right" ) {
                    isMove = true;
                    callback.apply(this);
                }
                if ( y - startY < -30 && direction == "up" ) {
                    isMove = true;
                    callback.apply(this);
                }
                if ( y - startY > 30 && direction == "down" ) {
                    isMove = true;
                    callback.apply(this);
                }
            });
        },
        delegate: function ( eventName, dom, selector, fn ) {
            var time = 15,
                elem;
            setInterval(function () {
                elem = jS(dom).find(selector);
            }, time);
            setTimeout(function () {
                if ( eventName == "tap" ) { 
                    elem.tap(function ( x, y ) { 
                        !jS(this)[0].thisDOMHasTapEvent && fn.apply(this, [x, y]);
                    })  
                }
                if ( eventName == "tapHold" ) { 
                    elem.tapHold(function ( x, y ) { 
                        !jS(this)[0].thisDOMHasTapHoldEvent && fn.apply(this, [x, y]); 
                    })  
                }
                if ( eventName == "swipe" ) {
                    elem.swipe(function ( x, y ) { 
                        !jS(this)[0].thisDOMHasSwipeEvent && fn.apply(this, [x, y]); 
                    })  
                }
                if ( eventName == "swipeLeft" ) {
                    elem.swipeLeft(function () { 
                        !jS(this)[0].thisDOMHasSwipeLeftEvent && fn.apply(this); 
                    })  
                }
                if ( eventName == "swipeRight" ) {
                    elem.swipeRight(function () { 
                        !jS(this)[0].thisDOMHasSwipeRightEvent && fn.apply(this); 
                    })  
                }
                if ( eventName == "swipeUp" ) {
                    elem.swipeUp(function () { 
                        !jS(this)[0].thisDOMHasSwipeUpEvent && fn.apply(this); 
                    })  
                }
                if ( eventName == "swipeDown" ) {
                    elem.swipeDown(function () { 
                        !jS(this)[0].thisDOMHasSwipeDownEvent && fn.apply(this); 
                    })  
                }
            }, time);
        }, 
        undelegate: function ( eventName, dom, selector ) { 
            var eNames = ["tap", "tapHold", "swipe", "swipeLeft", "swipeRight", "swipeUp", "swipeDown"],
                caches = ["thisDOMHasTapEvent", "thisDOMHasTapHoldEvent", "thisDOMHasSwipeEvent", "thisDOMHasSwipeLeftEvent", "thisDOMHasSwipeRightEvent", "thisDOMHasSwipeUpEvent", "thisDOMHasSwipeDownEvent"];
            var time = 15,
                elem;
            setInterval(function () {
                elem = jS(dom).find(selector);
            }, time);
            setTimeout(function () {
                elem.each(function () {
                    var getCache = caches[eNames.indexOf(eventName)];
                    jS(this)[0][getCache] = true;
                });
            }, time);
        },
        cubicBezier: {
            linear: "0.25, 0.25, 0.75, 0.75",
            ease: "0.25, 0.1, 0.25, 1",
            easeIn: "0.42, 0, 1, 1",
            easeOut: "0, 0, 0.58, 1",
            easeInOut: "0.42, 0, 0.58, 1",
            easeInQuad: "0.55, 0.085, 0.68, 0.53",
            easeInCubic: "0.55, 0.055, 0.675, 0.19",
            easeInQuart: "0.895, 0.03, 0.685, 0.22",
            easeInQuint: "0.755, 0.05, 0.855, 0.06",
            easeInSine: "0.47, 0, 0.745, 0.715",
            easeInExpo: "0.95, 0.05, 0.795, 0.035",
            easeInCirc: "0.6, 0.04, 0.98, 0.335",
            easeInBack: "0.6, -0.28, 0.735, 0.045",
            easeOutQuad: "0.25, 0.46, 0.45, 0.94",
            easeOutCubic: "0.215, 0.61, 0.355, 1",
            easeOutQuart: "0.165, 0.84, 0.44, 1",
            easeOutQuint: "0.23, 1, 0.32, 1",
            easeOutSine: "0.39, 0.575, 0.565, 1", 
            easeOutExpo: "0.19, 1, 0.22, 1",
            easeOutCirc: "0.075, 0.82, 0.165, 1",
            easeOutBack: "0.175, 0.885, 0.32, 1.275",
            easeInOutQuad: "0.455, 0.03, 0.515, 0.955",
            easeInOutCubic: "0.645, 0.045, 0.355, 1",
            easeInOutQuart: "0.77, 0, 0.175, 1",
            easeInOutQuint: "0.86, 0, 0.07, 1",
            easeInOutSine: "0.445, 0.05, 0.55, 0.95",
            easeInOutExpo: "1, 0, 0, 1",
            easeInOutCirc: "0.785, 0.135, 0.15, 0.86",
            easeInOutBack: "0.68, -0.55, 0.265, 1.55"
        }
    };

    var buildDom = function ( domArray ) {
        var length = domArray.length;
        this.length = length;
        for ( var i = 0; i < length; i++ ) {
            this[i] = domArray[i];
        }
        return this;
    };

    var jS = window.jS = function ( selector ) {
        var result = [],
            dom;
        if ( selector && arguments.length == 1 ) { 
            if ( fn.isFunc(selector) ) {
                if ( doc.readyState === "complete" ) {
                    selector();
                } else {
                    var domReady = (function () {
                        doc.addEventListener("DOMContentLoaded", function () {
                            doc.removeEventListener("DOMContentLoaded", domReady);
                            selector();
                        });
                    })();
                }
            } else if ( fn.isStr(selector) ) {
                dom = doc.querySelectorAll(selector);
                for ( var i = 0, j = dom.length; i < j; i++ ) {
                    result.push(dom[i]);
                }
            } else if ( selector.nodeType || selector === window || selector === doc ) {
                result.push(selector);
            } else if ( selector.length > 0 && selector[0].nodeType ) {
                for ( var i = 0, j = selector.length; i < j; i++ ) {
                    result.push(selector[i]);
                }
            }
        } 
        return new buildDom(result);
    };

    jS.fn = jS.prototype = buildDom.prototype = {

        version: version,

        load: function ( callback ) {
            if ( this[0] === window && fn.isFunc(callback) ) {
                var winLoad = (function () {
                    window.addEventListener("load", function () {
                        window.removeEventListener("load", winLoad);
                        callback();
                    });
                })();
            }
            return this;
        },

        orientation: function ( direction ) {
            if ( this[0] === window && fn.isPlainObj(direction) ) {
                window.addEventListener("orientationchange", function () {
                    var angle = window.orientation;
                    (angle == 0 || angle == 180) && (direction.v() || function () {});
                    (angle == 90 || angle == -90) && (direction.h() || function () {});
                });
            }
            return this;
        },

        deviceMotion: function ( callback ) {
            if ( this[0] === window && fn.isFunc(callback) ) {
                window.addEventListener("devicemotion", deviceMotionHandler);
                var speed = 0,
                    time = 0,
                    curTime = 0,
                    lastTime = 0,
                    x = 0,
                    y = 0,
                    z = 0,
                    X = 0,
                    Y = 0,
                    Z = 0;
                function deviceMotionHandler ( e ) {  
                    var acceleration = e.accelerationIncludingGravity;
                    curTime = new Date().getTime();
                    if ( curTime - lastTime > 300 ) {
                        time = curTime - lastTime;
                        lastTime = curTime;
                        x = acceleration.x;
                        y = acceleration.y;
                        z = acceleration.z;
                        speed = Math.abs((x + y + z - X - Y - Z) / time * 10000);
                        ( speed > 900 ) && callback();
                        X = x;
                        Y = y;
                        Z = z;
                    }
                }
            }
            return this;
        },

        each: function ( callback ) {
            for ( var i = 0, j = this.length; i < j; i++ ) {
                callback.call(this[i], i, this[i]);
            }
            return this;
        },

        size: function () {
            return (arguments.length == 0) && (this.length);
        },

        addClass: function ( className ) { 
            return this.each(function () { 
                fn.doClass(className, "add", this); 
            });
        },

        removeClass: function ( className ) { 
            return this.each(function () { 
                fn.doClass(className, "remove", this); 
            }); 
        },

        toggleClass: function ( className ) { 
            return this.each(function () { 
                fn.doClass(className, "toggle", this); 
            }); 
        },

        hasClass: function ( className ) {
            className = fn.trim(className, "bothSide");
            return className.indexOf(" ") == -1 ? this[0].classList.contains(className) : false;
        },

        attr: function ( attribute ) {
            if ( arguments.length == 1 ) {
                if ( fn.isPlainObj(attribute) ) {
                    return this.each(function () {
                        for ( var x in attribute ) {
                            this.setAttribute(fn.formatConverter(x), attribute[x]);
                        }
                    });
                }
                if ( fn.isStr(attribute) ) {
                    attribute = fn.trim(attribute, "bothSide");
                    return (attribute.indexOf(" ") == -1) && (this[0].getAttribute(fn.formatConverter(attribute)));
                }
            }
        },

        removeAttr: function ( attribute ) {
            if ( arguments.length == 1 && fn.isStr(attribute) ) {
                attribute = fn.formatSpace(attribute);
                return this.each(function () {
                    for ( var i = 0, j = attribute.length; i < j; i++ ) {
                        this.removeAttribute(fn.formatConverter(attribute[i]));
                    }
                });
            }
        },

        css: function ( styles ) {
            if ( arguments.length == 1 ) {
                if ( this[0] === window ) {
                    switch ( styles ) {
                        case "width": return window.innerWidth + "px"; break;
                        case "height": return window.innerHeight + "px"; break;
                        case "~~width": return window.innerWidth; break;
                        case "~~height": return window.innerHeight; break;
                    }
                }
                if ( fn.isPlainObj(styles) ) {
                    return this.each(function () {
                        for ( var x in styles ) {
                            if ( x == "float" ) {
                                this.style.cssFloat = styles[x];
                            } else if ( !(x in doc.documentElement.style) ) {
                                var y = x.charAt(0).toUpperCase() + x.substring(1);
                                this.style["webkit" + y] = styles[x];
                                this.style[x] = styles[x];
                            } else {
                                this.style[x] = styles[x];
                            }
                        }
                    });
                }
                if ( fn.isStr(styles) ) {
                    styles = fn.trim(styles, "bothSide");
                    if ( styles.indexOf(" ") == -1 ) {
                        var check = styles.match("~~") ? true : false;
                        styles = styles.replace(/(\~\~|\-webkit\-)/, "");
                        styles = styles.replace(/-(\w)/g, function ( styles ) { 
                            return styles.slice(1).toUpperCase();
                        });
                        function getStyle ( elem, css ) {
                            return doc.defaultView.getComputedStyle(elem, null).getPropertyValue(fn.formatConverter(css));
                        }
                        var that = jS(this)[0],
                            display = getStyle(that, "display"),
                            realStyle = "";
                        if ( !(styles in doc.documentElement.style) ) {
                            styles = "webkit" + styles.charAt(0).toUpperCase() + styles.substring(1);
                        }
                        if ( !styles.match(/(width|height|transform)/) ) {
                            realStyle = getStyle(that, fn.formatConverter(styles));
                            if ( check ) {
                                return realStyle ? parseFloat(realStyle) : parseFloat(that.style[styles]);
                            } else {
                                return realStyle ? realStyle : that.style[styles];
                            }
                        } else {
                            if ( display != "none" ) {
                                return check ? parseFloat(getStyle(that, styles)) : getStyle(that, styles);
                            } else {
                                var position = that.style.position,
                                    visibility = that.style.visibility,
                                    display = that.style.display,
                                    styleVal = "";
                                jS(that).css({
                                    position: "absolute",
                                    visibility: "hidden",
                                    display: "block"
                                });
                                realStyle = getStyle(that, styles);
                                var arr = [position, visibility, display],
                                    _arr = ["position", "visibility", "display"]; 
                                for ( var i = 0; i < 3; i++ ) {
                                    arr[i] != "" ? that.style[_arr[i]] = arr[i] : that.style.removeProperty(_arr[i]);
                                }
                                return check ? parseFloat(realStyle) : realStyle;
                            }
                        }
                    }
                }
            }
        },

        removeCSS: function ( styleName ) {
            if ( arguments.length == 1 && fn.isStr(styleName) ) {
                styleName = fn.formatSpace(styleName);
                return this.each(function () {
                    for ( var i = 0, j = styleName.length; i < j; i++ ) {
                        var name = styleName[i];
                        name = (!(name in doc.documentElement.style) && name.indexOf("webkit") == -1 ? "-webkit-" + fn.formatConverter(name) : name); 
                        this.style.removeProperty(fn.formatConverter(name));
                    }
                });
            }
        },

        html: function ( value ) {
            if ( value === undefined ) {
                return jS(this)[0].innerHTML;
            } else {
                return this.each(function () {
                    this.innerHTML = value;
                });
            }
        },

        text: function ( value ) {
            if ( value === undefined ) {
                return jS(this)[0].innerText;
            } else {
                return this.each(function () {
                    this.innerText = value;
                });
            }
        },

        val: function ( value ) {
            if ( value === undefined ) {
                return jS(this)[0].value;
            } else {
                return this.each(function () {
                    value = value.replace(/<[^>]+>/g, "");
                    this.value = value;
                });
            }
        },

        replaceWith: function ( value ) {
            return this.each(function () {
                (fn.isStr(value)) && (this.outerHTML = value);
            });
        },

        innerWidth: function () {
            return (arguments.length == 0) && (parseFloat(jS(this[0]).css("width")) + parseFloat(jS(this[0]).css("paddingLeft")) + parseFloat(jS(this[0]).css("paddingRight")));
        },

        innerHeight: function () {
            return (arguments.length == 0) && (parseFloat(jS(this[0]).css("height")) + parseFloat(jS(this[0]).css("paddingTop")) + parseFloat(jS(this[0]).css("paddingBottom")));
        },

        outerWidth: function ( bool ) {
            return bool === undefined ? parseFloat(this[0].offsetWidth) : ((fn.isBool(bool) && bool) && (parseFloat(this[0].offsetWidth) + parseFloat(jS(this[0]).css("marginLeft")) + parseFloat(jS(this[0]).css("marginRight"))))
        },

        outerHeight: function ( bool ) {
            return bool === undefined ? parseFloat(this[0].offsetHeight) : ((fn.isBool(bool) && bool) && (parseFloat(this[0].offsetHeight) + parseFloat(jS(this[0]).css("marginTop")) + parseFloat(jS(this[0]).css("marginBottom"))))
        },

        hide: function () {
            if ( arguments.length == 0 ) {
                return this.each(function () {
                    if ( jS(this).css("display") != "none" ) {
                        this.style.display = "none";
                    }
                });
            }
        },

        show: function () {
            if ( arguments.length == 0 ) {
                return this.each(function () {
                    var _this = jS(this),
                        nodeName = this.nodeName.toLowerCase(),
                        getDisplay = "";
                    if ( _this.css("display") == "none" ) {
                        if ( _this[0].style.display == "none" ) {
                            _this.removeCSS("display");
                            checkResultDisplay(_this);
                        } else {
                            checkResultDisplay(_this);
                        }
                        function checkResultDisplay ( dom ) {
                            if ( jS(dom).css("display") == "none" ) {
                                document.body.insertAdjacentHTML("beforeend", '<iframe id="js-show-iframe-js" width="0" height="0" frameborder="0"><\/iframe>');
                                var iframe = jS("#js-show-iframe-js"),
                                    doc = jS("#js-show-iframe-js")[0].contentDocument,
                                    elem = doc.createElement(nodeName);
                                doc.body.appendChild(elem);
                                getDisplay = jS(elem).css("display");
                                dom.css({
                                    display: getDisplay
                                });
                                jS(iframe).remove();
                            }
                        }
                    }
                });
            }
        },

        toggle: function () {
            if ( arguments.length == 0 ) {
                return this.each(function () {
                    var _this = jS(this);
                    _this.css("display") == "none" ? _this.show() : _this.hide();
                });
            }
        },

        animate: function ( effect, time, easing, callback ) { 
            window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame;
            var arg = arguments,
                arr = [];
            for ( var i = 0, j = arg.length; i < j; i++ ) {
                arr.push(typeof arg[i]);
            } 
            var Effect = effect,
                Time = (fn.isNum(arg[1]) ? time : 500),
                Easing = (arr.indexOf("string") > 0 ? arg[arr.indexOf("string")] : "ease"),
                Callback = (arr.indexOf("function") > 0 ? arg[arr.indexOf("function")] : function () {});   
            return this.each(function () {
                var _this = jS(this),
                    that = this;  
                if ( _this.css("display") != "none" && !that.thisDomInAnimation ) {
                    that.thisDomInAnimation = true;
                    var delay = that.thisDomAnimationDelay || "0ms";
                    var cubic = fn.cubicBezier;
                    for ( var x in cubic ) {
                        (Easing == x) && (Easing = cubic[x]);
                    }
                    that.style.transition = "all " + Time + "ms " + "cubic-bezier(" + Easing + ") " + delay;
                    for ( var name in Effect ) {
                        if ( !name.match(/(transition|transitionProperty|transitionDuration|transitionTimingFunction|transitionDelay|gradient|filter|display|visibility)/g) ) {
                            _this[0].style[name] = Effect[name];
                        }
                    }
                    var isOver = false;
                    var checkEnd = (function () {
                        that.addEventListener("transitionend", function () {
                            if ( !isOver ) {
                                isOver = true;
                                that.removeEventListener("transitionend", checkEnd);
                                requestAnimationFrame(function () {
                                    _this.removeCSS("transition");
                                    that.thisDomInAnimation = undefined;
                                    that.thisDomAnimationDelay = undefined;
                                    Callback.apply(that);
                                });
                            }
                        });
                    })();
                }
            });
        },

        delay: function ( time ) {
            if ( fn.isNum(time) ) {
                return this.each(function () {
                    jS(this)[0].thisDomAnimationDelay = time + "ms";
                });
            }
        },

        inAnimate: function () {
            if ( arguments.length == 0 ) {
                return jS(this)[0].thisDomInAnimation ? true : false;
            }
        },

        is: function ( checker ) {
            if ( fn.isStr(checker) ) {
                var _this = this[0];
                return _this.matchesSelector ? _this.matchesSelector(checker) : ((_this.webkitMatchesSelector) && (_this.webkitMatchesSelector(checker)));
            }
        },

        parent: function () {
            if ( arguments.length == 0 ) {
                var parentArr = [];
                this.each(function () {
                    parentArr.push(jS(this)[0].parentNode);
                });
                return jS(parentArr);
            }
        },

        children: function () {
            var mark = arguments[0],
                childArr = [];
            this.each(function () {
                var child = jS(this)[0].children;
                for ( var i = 0, j = child.length; i < j; i++ ) {
                    var ci = child[i];
                    mark ? (jS(ci).is(mark) && childArr.push(ci)) : (childArr.push(ci));
                }
            });
            return jS(childArr);
        },

        siblings: function () {
            var mark = arguments[0],
                sibling = [];
            this.each(function () {
                var _this = jS(this)[0],
                    sg = _this.parentNode.children;
                for ( var i = 0, j = sg.length; i < j; i++ ) {
                    (sg[i] !== _this) && (mark ? (jS(sg[i]).is(mark) && sibling.push(sg[i])) : sibling.push(sg[i]));
                }
            });
            return jS(sibling);
        },

        prev: function () {
            if ( arguments.length == 0 ) {
                var prevArr = [];
                this.each(function () {
                    var p = jS(this)[0].previousElementSibling;
                    p && prevArr.push(p);
                });
                return jS(prevArr);
            }
        },

        next: function () {
            if ( arguments.length == 0 ) {
                var nextArr = [];
                this.each(function () {
                    var n = jS(this)[0].nextElementSibling;
                    n && nextArr.push(n);
                });
                return jS(nextArr);
            }
        },

        first: function () {
            if ( arguments.length == 0 ) {
                var firstArr = [];
                firstArr.push(this[0]);
                return jS(firstArr);
            }
        },

        last: function () {
            var lastArr = [];
            lastArr.push(this[jS(this).size() - 1]);
            return jS(lastArr);
        },

        firstChild: function () {
            return (arguments.length == 0) && jS(this[0].firstElementChild);
        },

        lastChild: function () {
            return (arguments.length == 0) && jS(this[0].lastElementChild);
        },

        find: function ( dom ) {
            var findArr = [];
            this.each(function () {
                var findNodes = jS(this)[0].querySelectorAll(dom);
                for ( var i = 0, j = findNodes.length; i < j; i++ ) {
                    (findNodes[i].nodeType == 1) && findArr.push(findNodes[i]);
                }
            });
            return jS(findArr);
        },

        eq: function ( index ) {
            if ( fn.isNum(index) && index >= 0 ) {
                var eqArr = [];
                if ( index < this.length ) {
                    eqArr.push(this[index]);
                    return jS(eqArr);
                }
            }
        },

        index: function () {
            if ( this[0] ) {
                var child = this[0],
                    i = 0;
                while ( (child = child.previousSibling) != null ) {
                    child.nodeType == 1 && i++;
                }
                return i;
            }
        },

        clone: function ( deep ) {
            var cloneElem = [];
            deep = deep && fn.isBool(deep) ? true : false;
            this.each(function () {
                cloneElem.push(this.cloneNode(deep));
            });
            return cloneElem;
        },

        append: function ( elem ) {
            if ( elem ) {
                return this.each(function () {
                    if ( fn.isStr(elem) ) {
                        this.insertAdjacentHTML("beforeend", elem);
                    }
                    if ( Array.isArray(elem) ) {
                        for ( var i = 0, j = elem.length; i < j; i++ ) {
                            this.appendChild(elem[i]);
                        }
                    }
                });
            }
        },

        prepend: function ( elem ) {
            if ( elem ) {
                return this.each(function () {
                    if ( fn.isStr(elem) ) {
                        this.insertAdjacentHTML("afterbegin", elem);
                    }
                    if ( Array.isArray(elem) ) {
                        elem.reverse();
                        for ( var i = 0, j = elem.length; i < j; i++ ) {
                            this.insertBefore(elem[i], this.firstChild);
                        }
                    }
                });
            }
        },

        after: function ( elem ) {
            if ( elem ) {
                return this.each(function () {
                    if ( fn.isStr(elem) ) {
                        this.insertAdjacentHTML("afterend", elem);
                    }
                    if ( Array.isArray(elem) ) {
                        elem.reverse();
                        for ( var i = 0, j = elem.length; i < j; i++ ) {
                            this.parentNode.insertBefore(elem[i], this.nextSibling);
                        }
                    }
                });
            }
        },

        before: function ( elem ) {
            if ( elem ) {
                return this.each(function () {
                    if ( fn.isStr(elem) ) {
                        this.insertAdjacentHTML("beforebegin", elem);
                    }
                    if ( Array.isArray(elem) ) {
                        for ( var i = 0, j = elem.length; i < j; i++ ) {
                            this.parentNode.insertBefore(elem[i], this);
                        }
                    }
                });
            }
        },

        remove: function () {
            if ( arguments.length == 0 ) {
                return this.each(function () {
                    this.remove();
                });
            }
        },

        empty: function () {
            if ( arguments.length == 0 ) {
                return this.each(function () {
                    jS(this).html("");
                });
            }
        },

        add: function ( elem ) {
            var dom = this,
                addElem = jS(elem);
            for ( var i = 0, j = addElem.length; i < j; i++ ) {
                dom[dom.length] = addElem[i];
                dom.length++;
            }
            return dom;  
        },

        tap: function ( callback ) {
            var timer = 0, startX, startY, x, y, isMove;
            var arg = arguments.length, 
                arg0 = arguments[1];
            return this.each(function () {
                var that = jS(this)[0];
                that.addEventListener("touchstart", function ( e ) {
                    isMove = false;
                    startX = e.targetTouches[0].pageX;
                    startY = e.targetTouches[0].pageY;
                    timer = Date.now();
                });
                that.addEventListener("touchmove", function ( e ) {
                    if ( isMove ) {
                        return;
                    }
                    x = e.changedTouches[0].pageX;
                    y = e.changedTouches[0].pageY;
                    if ( x - startX != 0 || y - startY != 0 ) {
                        isMove = true;
                    }
                });
                that.addEventListener("touchend", function ( e ) {
                    var _this = this;
                    if ( !isMove ) {
                        ((arg == 2 && fn.isBool(arg0) && !arg0 ) ? false : true) && e.preventDefault();
                        if ( Date.now() - timer < 300 ) {
                            callback.apply(_this, [e.changedTouches[0].pageX, e.changedTouches[0].pageY]);
                            timer = 0;
                        }
                    }
                });
            });
        },

        tapHold: function ( fn ) {
            var timer = 0, startX, startY, x, y, isMove, holdTouch;
            var arg = arguments.length, 
                arg0 = arguments[1];
            return this.each(function () {
                var that = jS(this)[0];
                that.addEventListener("touchstart", function ( e ) {
                    var _this = this;
                    e.preventDefault();
                    isMove = false;
                    startX = e.targetTouches[0].pageX;
                    startY = e.targetTouches[0].pageY;
                    timer = Date.now();
                    holdTouch = setTimeout(function () {
                        x = e.targetTouches[0].pageX;
                        y = e.targetTouches[0].pageY;
                        (x == startX && y == startY) && fn.apply(_this, [x, y]);
                    }, 750);
                });
                that.addEventListener("touchend", function ( e ) {
                    clearTimeout(holdTouch);
                    e.preventDefault();
                });
            });
        },

        swipe: function ( callback ) {
            return this.each(function () {
                fn.swipe("", this, callback);
            });
        },

        swipeLeft: function ( callback ) {
            return this.each(function () {
                fn.swipe("left", this, callback);
            });
        },

        swipeRight: function ( callback ) {
            return this.each(function () {
                fn.swipe("right", this, callback);
            });
        },

        swipeUp: function ( callback ) {
            return this.each(function () {
                fn.swipe("up", this, callback);
            });
        },

        swipeDown: function ( callback ) {
            return this.each(function () {
                fn.swipe("down", this, callback);
            });
        },

        on: function ( eventName, targetDom, callback ) {
            if ( arguments.length == 3 && fn.isStr(targetDom) && eventName.match(/(tap|tapHold|swipe|swipeLeft|swipeRight|swipeUp|swipeDown)/) ) {    
                return this.each(function () {
                    var _this = jS(this),
                        that = _this[0];
                    (eventName == "tap") && fn.delegate("tap", that, targetDom, callback); 
                    (eventName == "tapHold") && fn.delegate("tapHold", that, targetDom, callback); 
                    (eventName.indexOf("swipe") > -1) && fn.delegate(eventName, that, targetDom, callback); 
                });
            }
        },

        off: function ( eventName, targetDom ) {
            if ( arguments.length == 2 && fn.isStr(targetDom) && eventName.match(/(tap|tapHold|swipe|swipeLeft|swipeRight|swipeUp|swipeDown)/) ) {    
                return this.each(function () {
                    var _this = jS(this),
                        that = _this[0];
                    (eventName == "tap") && fn.undelegate("tap", that, targetDom); 
                    (eventName == "tapHold") && fn.undelegate("tapHold", that, targetDom); 
                    (eventName.indexOf("swipe") > -1) && fn.undelegate(eventName, that, targetDom); 
                });
            }
        }

    };

    var ua = navigator.userAgent.toLowerCase();
    var rootJS = window.jS;

    jS.extend = function ( methods ) {
        if ( methods ) {
            for ( var x in methods ) {
                jS[x] = methods[x];
            }
        }
    };

    jS.extend({
        isMobile: function () {
            if ( arguments.length == 0 ) {
                var mobile = ua.match(/(ios|iphone|ipod|ipad|mobile|android|windows phone|symbianos|ucweb|windows me|opera mini|opera mobl|nexus|series|nokia|blackberry)/) ? true : false;
                return mobile && "ontouchend" in document && "onorientationchange" in window ? true : false;
            }
        },

        isIOS: function ( version ) {
            var isIOS = jS.isMobile() && ua.match(/(ios|iphone|ipad|ipod)/);
            if ( arguments.length == 0 ) {
                return isIOS;
            }
            if ( fn.isBooln(version) && version ) {
                return isIOS ? ua.match(/os(\S*)likemacosx/)[1].replace(/(_)/g, ".") : "not ios";
            }
        },

        isAndroid: function ( version ) {
            var isAndroid = jS.isMobile() && !jS.isIOS() && !ua.match(/(windows|blackberry|symbianos)/) ? true : false;
            if ( arguments.length == 0 ) {
                return isAndroid;
            }
            if ( fn.isBool(version) && version ) {
                var str01 = ua.indexOf("android"),
                    str02 = ua.substring(str01 + 7),
                    str03 = str02.indexOf(";");
                return isAndroid ? str02.substring(0, str03) : "not android";
            }
        },

        noConflict: function () {
            if ( arguments.length == 0 ) {
                window.jS = rootJS;
                return jS;
            }
        },

        sessionStorage: {
            set: function ( param ) {
                if ( fn.isPlainObj(param) ) {
                    for ( var x in param ) {
                        sessionStorage.setItem(x, param[x]);
                    }
                }
            },
            get: function ( key ) {
                return sessionStorage.getItem(key);
            },
            remove: function ( keys ) {
                keys = fn.formatSpace(keys);
                keys.forEach(function ( item ) {
                    sessionStorage.removeItem(item);
                })
            },
            clear: function () {
                (arguments.length == 0) && sessionStorage.clear();
            }
        },

        localStorage: {
            set: function ( param ) {
                if ( fn.isPlainObj(param) ) {
                    for ( var x in param ) {
                        localStorage.setItem(x, param[x]);
                    }
                }
            },
            get: function ( key ) {
                return localStorage.getItem(key);
            },
            remove: function ( keys ) {
                keys = fn.formatSpace(keys);
                keys.forEach(function ( item ) {
                    localStorage.removeItem(item);
                })
            },
            clear: function () {
                (arguments.length == 0) && localStorage.clear();
            }
        },

        ajax: function ( param ) {
            if ( param && fn.isPlainObj(param) ) {
                var url = param.url || "",
                    type = (param.type && (param.type).toUpperCase()) || "GET",
                    async = param.async || true,
                    data = param.data || null,
                    dataType = param.dataType || "text",
                    success = param.success || function () {},
                    error = param.error || function () {};
                var xmlHttp = new XMLHttpRequest();
                var dataInfo;
                if ( fn.isStr(data) ) {
                    dataInfo = data;
                } else if ( fn.isPlainObj(data) ) {
                    var arr = [];
                    for ( var x in data ) {
                        var y = data[x];
                        arr.push(encodeURIComponent(x) + "=" + encodeURIComponent(y));
                    }
                    dataInfo = arr.join("&");
                }
                if ( type == "GET" ) {
                    url += (url.indexOf("?") == -1 ? "?" : "&") + dataInfo;
                    xmlHttp.open(type, url, async);
                    xmlHttp.send(null);
                }
                if ( type == "POST" ) {
                    xmlHttp.open(type, url, async);
                    xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
                    xmlHttp.send(dataInfo);
                }
                xmlHttp.onreadystatechange = function () {
                    var status = xmlHttp.status;
                    var txt = fn.trim(xmlHttp.responseText, "bothSide");
                    if ( xmlHttp.readyState == 4 && ((status >= 200 && status < 300)) || status == 304 ) {
                        (dataType == "text") && success(txt);
                        (dataType == "json") && success(JSON.parse(txt));
                    } else {
                        (dataType == "text") && error(txt);
                        (dataType == "json") && error(JSON.parse(txt));
                    }
                };
            }
        },

        toJSON: function ( obj, filter, mark ) {
            var getJSON = "";
            if ( obj && fn.isPlainObj(obj) ) { 
                var length = arguments.length;
                switch ( length ) {
                    case 1:
                        getJSON = JSON.stringify(obj);
                        break;
                    case 2:
                        getJSON = Array.isArray(filter) ? JSON.stringify(obj, filter) : JSON.stringify(obj, null, filter);
                        break;
                    case 3:
                        getJSON = JSON.stringify(obj, filter, mark);
                        break;
                    default:
                        getJSON = JSON.stringify(obj);
                }
            }
            return getJSON;
        }

    });

    return jS;

});